<?php
namespace core;

class Container{
    // 存储绑定关系（抽象类型 => 具体实现/闭包）
    protected $bindings = [];
    // 存储单例实例(抽象类型 => 实例)
    protected $singletons = [];
    // 缓存已创建的实例(避免重复实例化)
    protected $instances = [];

    /**
     * 绑定一个抽象类型到具体实现（非单例）
     * @param string $abstract 抽象类型（类名）
     * @param callable|string $concrete 具体实现或闭包
     * @return  void
     */
    public function bind(string $abstract, $concrete): void
    {
        $this->bindings[$abstract] = $concrete;
    }

    /**
     * 绑定单例（多次获取返回同一实例）
     * @param string $abstract  抽象类型
     * @param callable|string|null $concrete    具体实现（默认与abstract相同）   
     * @return void
     */
    public function singleton(string $abstract, $concrete = null): void
    {
        $this->singletons[$abstract] = $concrete ?? $abstract;
    }

    /**
     * 获取一个实例（自动解析依赖）
     * @param string $abstract  抽象类型
     * @return mixed    实例
     * @throws \Exception
     */
    public function get(string $abstract){
        // 优先从单例缓存中获取
        if(isset($this->instances[$abstract])){
            return $this->instances[$abstract];
        }

        // 获取具体实现（闭包或类名）
        $concrete = $this->getConcrete($abstract);

        // 解析构造函数依赖
        $dependencies = $this->resolveDependencies($concrete);

        // 创建实例
        $instance = $this->createInstance($concrete, $dependencies);

        // 如果是单例，缓存实例
        if($this->isSingleton($abstract)){
            $this->instances[$abstract] = $instance;
        }

        return $instance;
    }

    /**
     * 检查是否为单例绑定
     * @param string $abstract
     * @return bool
     */
    public function isSingleton(string $abstract): bool
    {
        return isset($this->singletons[$abstract]);
    }

    /**
     * 获取具体实现（闭包或类名）
     * @param string $abstract
     * @return callable|string
     */
    public function getConcrete(string $abstract){
        if(isset($this->bindings[$abstract])){
            return $this->bindings[$abstract];
        }
        return $abstract;   //默认绑定自身（用于无接口的场景）
    }

    /**
     * 解析构造函数的依赖项
     * @param callable|string $concrete
     * @throws \Exception
     * @return array    依赖项数组
     */
    public function resolveDependencies($concrete): array
    {
        // 如果是闭包，先执行闭包获取类名（可能动态创建类）
        if(is_callable($concrete)){
            $concrete = $concrete($this);   // 传递容器给闭包，允许闭包内部使用容器
        }

        $reflector = new \ReflectionClass($concrete);
        if(!$reflector->isInstantiable()){
            throw new \Exception("Class {$reflector->getName()} cannot be instantiaed.");
        }

        $constructor =  $reflector->getConstructor();
        if($constructor === null){
            return [];  // 无构造函数，无需依赖
        }

        $dependencies = [];
        foreach($constructor->getParameters() as $parameter){
            $type = $parameter->getType();
            if($type === null){
                // 处理无类型提示的参数（如标量或带默认值的参数）
                if($parameter->isDefaultValueAvailable()){
                    $dependencies[] = $parameter->getDefaultValue();
                }else{
                    throw new \EXception("Cannot resolve dependency for parameter '{$parameter->getName()}' in class '{$reflector->getName()}'");
                }
                continue;
            }

            // 递归解析依赖的类型
            $dependencyClass = $type->getName();
            $dependencies[] = $this->get($dependencyClass);
        }

        return $dependencies;
    }

    protected function createInstance($concrete, array $dependencies){
        if(is_callable($concrete)){
            return $concrete($this, ...$dependencies);  // 闭包可能接收容器和依赖项
        }
        $reflector = new \ReflectionClass($concrete);
        return $reflector->newInstanceArgs($dependencies);
    }
}